<?php

namespace Muyuxuanpay\threePay\src\bft\protocolPayment;

use Muyuxuanpay\threePay\BaseTrait;
use Muyuxuanpay\threePay\ProtocolPaymentPay;
use Muyuxuanpay\tool\Mtool;

class Pay implements ProtocolPaymentPay
{
    use BaseTrait;
    use ProtocolPaymentTrait;
    protected $must = [
        'public_certs_path'=>"公钥文件地址",
        'privite_certs_path'=>"私钥文件地址",
        "terminal_id"=>"终端号",
        "member_id"=>"商户号",
        "key"=>"私钥密码",
    ];//必填参数
    protected $URL = [
        'wx_pub'=>'https://vgw.baofoo.com/cutpayment/protocol/backTransRequest'
    ];
    protected $defaultConfig = [
        'version'=>'4.0.0.0',
        'dgtl_envlp'=>'01',
    ];
    protected $crad_type_list = [
        '借记卡'=>101,
        '信用卡'=>102
    ];
    protected $id_card_type_list = [
        '身份证'=>'01',
        '港澳通行证'=>'12',
        '台胞通行证'=>'13',
    ];

    /**
     * 预签约
     * @param string $out_trade_no 订单号
     * @param string $id_crad_no 证件号
     * @param string $id_crad_type 证件类型
     * @param string $card_name 持卡人名
     * @param string $mobile 手机号
     * @param string $card_no 卡号
     * @param string $notify_url 回调地址
     * @param array  $event [card_exp_date=yymm卡有效期] [card_cvv=3位 卡CVV] [card_type=卡类型|101=储蓄卡/102=信用卡]
     * @return void
     * @throws \Exception
     */
    public function preContract(string $out_trade_no,string $id_crad_no,string $id_crad_type,string $card_name,string $mobile,string $card_no,string $notify_url='',array $event=[])
    {
        //验证参数
        $this->verify();
        if(empty($event['card_type'])){
            throw new \Exception("请设置卡类型参数");
        }
        if(!is_numeric($event['card_type'])){
            if(empty($this->card_type_list[$event['card_type']])){
                throw new \Exception("请设置身份证类型参数");
            }
            $card_type = $this->card_type_list[$event['card_type']];
        }else{
            $card_type = $event['card_type'];
        }
        //处理卡类型
        if($card_type==102){
            if(empty($event['card_exp_date'])){
                throw new \Exception("请设置有效期参数");
            }
            if(empty($event['card_cvv'])){
                throw new \Exception("请设置cvv参数");
            }
            $card_exp_date = $event['card_exp_date'];
            $card_cvv = $event['card_cvv'];
            $acc_info = "{$card_no}|{$card_name}|{$id_crad_no}|{$mobile}|{$card_cvv}|{$card_exp_date}";
        }else{
            $acc_info = "{$card_no}|{$card_name}|{$id_crad_no}|{$mobile}";
        }
        //处理证件类型
        if(!is_numeric($id_crad_type)){
            if(empty($this->id_card_type_list[$id_crad_type])){
                throw new \Exception("请设置身份证类型参数");
            }
            $id_crad_type = $this->id_card_type_list[$id_crad_type];
        }
        $aesKey = Mtool::getRandomStr(16);

        $newData = [
            'send_time'=>date("Y-m-d H:i:s"),
            'msg_id'=>$out_trade_no,
            'version'=>$this->defaultConfig['version'],
            'terminal_id'=>$this->config['terminal_id'],
            'txn_type'=>'11',
            'member_id'=>$this->config['member_id'],
            'dgtl_envlp'=>$this->encryptByCERFile("01|".$aesKey,$this->config['public_certs_path']),
            'card_type'=>$card_type,
            'id_card_type'=>$id_crad_type,
            'acc_info'=>$this->AesEncrypt($acc_info,$aesKey),
        ];
        if(!empty($event['user_id'])){
            $newData['user_id'] = $event['user_id'];
        }
        $newData['signature'] = $this->sign($newData);
        $res = $this->post($newData,$this->URL['wx_pub']);
        if(isset($res['biz_resp_code'])&&$res['biz_resp_code']=='0000'){
            return $res;
        }else{
            throw new \Exception($res['biz_resp_msg']);
        }
    }

    /**
     * 确认签约
     * @param string $out_trade_no 订单号
     * @param string $presign_seq_no 预签约唯一码【预签约返回的唯一码unique_code】
     * @param string $sms_code 短信验证码
     * @return array
     * @throws \Exception
     */
    public function contractConfirm(string $out_trade_no,string $presign_seq_no,string $sms_code)
    {
        //验证参数
        $this->verify();
        $unique_code = [
            $presign_seq_no,
            $sms_code
        ];
        if(empty($out_trade_no)){
            $out_trade_no = Mtool::getRandomStr(32);
        }
        $unique_code = implode('|',$unique_code);
        $aesKey = Mtool::getRandomStr(16);

        $newData = [
            'send_time'=>date("Y-m-d H:i:s"),
            'msg_id'=>$out_trade_no,
            'version'=>$this->defaultConfig['version'],
            'terminal_id'=>$this->config['terminal_id'],
            'txn_type'=>'02',
            'member_id'=>$this->config['member_id'],
            'dgtl_envlp'=>$this->encryptByCERFile("01|".$aesKey,$this->config['public_certs_path']),
            'unique_code'=>$this->AesEncrypt($unique_code,$aesKey),
        ];
        $newData['signature'] = $this->sign($newData);
        $res = $this->post($newData,$this->URL['wx_pub']);
        if(isset($res['biz_resp_code'])&&$res['biz_resp_code']=='0000'){
            return $res;
        }else{
            throw new \Exception($res['biz_resp_msg']);
        }
    }

    /**
     * 查询签约
     * @param string $out_trade_no 流水号
     * @param string|null $chanpay_trade_no
     * @param array $event [acc_no=卡号]
     * @return array|mixed
     * @throws \Exception
     */
    public function contractQuery(string $out_trade_no,?string $chanpay_trade_no=null,array $event=[])
    {
        //验证参数
        $this->verify();
        if(empty($event['acc_no'])){
            throw new \Exception("请设置卡号参数");
        }
        $acc_no = [
            $event['acc_no']
        ];
        if(empty($out_trade_no)){
            $out_trade_no = Mtool::getRandomStr(32);
        }
        $acc_no = implode('|',$acc_no);
        $aesKey = Mtool::getRandomStr(16);

        $newData = [
            'send_time'=>date("Y-m-d H:i:s"),
            'msg_id'=>$out_trade_no,
            'version'=>$this->defaultConfig['version'],
            'terminal_id'=>$this->config['terminal_id'],
            'txn_type'=>'03',
            'member_id'=>$this->config['member_id'],
            'dgtl_envlp'=>$this->encryptByCERFile("01|".$aesKey,$this->config['public_certs_path']),
            'acc_no'=>$this->AesEncrypt($acc_no,$aesKey),
        ];
//        echo "<pre>";
//        print_r($newData);
//        exit;
        $newData['signature'] = $this->sign($newData);
        $res = $this->post($newData,$this->URL['wx_pub']);
        if(isset($res['biz_resp_code'])&&$res['biz_resp_code']=='0000'){
            return $res;
        }else{
            throw new \Exception($res['biz_resp_msg']);
        }
    }

    /**
     * 解约
     * @param string $out_trade_no 流水号
     * @param string $sign_id 协议号
     * @param string $sign_acct_shrt_id 不用传
     * @param array $event 不用传
     * @return array|mixed
     * @throws \Exception
     */
    public function unContract(string $out_trade_no,string $sign_id,string $sign_acct_shrt_id='',array $event=[])
    {
        //验证参数
        $this->verify();
        $protocol_no = [
            $sign_id
        ];
        if(empty($out_trade_no)){
            $out_trade_no = Mtool::getRandomStr(32);
        }
        $protocol_no = implode('|',$protocol_no);
        $aesKey = Mtool::getRandomStr(16);

        $newData = [
            'send_time'=>date("Y-m-d H:i:s"),
            'msg_id'=>$out_trade_no,
            'version'=>$this->defaultConfig['version'],
            'terminal_id'=>$this->config['terminal_id'],
            'txn_type'=>'04',
            'member_id'=>$this->config['member_id'],
            'dgtl_envlp'=>$this->encryptByCERFile("01|".$aesKey,$this->config['public_certs_path']),
            'protocol_no'=>$this->AesEncrypt($protocol_no,$aesKey),
        ];
//        echo "<pre>";
//        print_r($newData);
//        exit;
        $newData['signature'] = $this->sign($newData);
        $res = $this->post($newData,$this->URL['wx_pub']);
        if(isset($res['biz_resp_code'])&&$res['biz_resp_code']=='0000'){
            return $res;
        }else{
            throw new \Exception($res['biz_resp_msg']);
        }
    }

    /**
     * 快捷支付
     * @param string $out_trade_no 订单流水号
     * @param string $sign_id 协议号
     * @param string|int|float $total_amount 订单金额
     * @param string $body 不用传或者传类目
     * @param array $event [goodsCategory=类目]传了body可不传[card_exp_date=yymm卡有效期] [card_cvv=3位 卡CVV]
     * @return array|mixed
     * @throws \Exception
     */
    public function quickPay(string $out_trade_no,string $sign_id,$total_amount,string $body='',array $event=[]){
        //验证参数
        $this->verify();
        $protocol_no = [
            $sign_id
        ];
        $msg_id = Mtool::getOrderNum();
        $protocol_no = implode('|',$protocol_no);
        $aesKey = Mtool::getRandomStr(16);
        if(empty($event['goodsCategory'])){
            if(empty($body)){
                $event['goodsCategory'] = '01';
            }else{
                $event['goodsCategory'] = $body;
            }
        }

        $newData = [
            'send_time'=>date("Y-m-d H:i:s"),
            'msg_id'=>$msg_id,
            'version'=>$this->defaultConfig['version'],
            'terminal_id'=>$this->config['terminal_id'],
            'txn_type'=>'143',
            'member_id'=>$this->config['member_id'],
            'trans_id'=>$out_trade_no,
            'order_amt'=>intval($total_amount*100),
            'txn_amt'=>intval($total_amount*100),
            'protocol_no'=>$this->AesEncrypt($protocol_no,$aesKey),
            'risk_item'=>json_encode([
                'goodsCategory'=>$event['goodsCategory'],
                'chPayIp'=>Mtool::getIp()
            ]),
            'dgtl_envlp'=>$this->encryptByCERFile("01|".$aesKey,$this->config['public_certs_path']),
        ];
        if(!empty($event['card_cvv'])&&!empty($event['card_exp_date'])){
            $newData['card_info'] = $this->AesEncrypt($event['card_exp_date'].'|'.$event['card_cvv'],$aesKey);
        }
//        echo "<pre>";
//        print_r($newData);
//        exit;
        $newData['signature'] = $this->sign($newData);
        $res = $this->post($newData,$this->URL['wx_pub']);
        if(isset($res['biz_resp_code'])&&$res['biz_resp_code']=='0000'){
            return $res;
        }else{
            throw new \Exception($res['biz_resp_msg']);
        }
    }

    /**
     * 订单查询
     * @param string $out_trade_no 订单流水号
     * @param string|null $chanpay_trade_no 无
     * @param array $event [create_date=创建时间]
     * @return array|mixed
     * @throws \Exception
     */
    public function orderQuery(string $out_trade_no,?string $chanpay_trade_no=null,array $event=[]){
        //验证参数
        $this->verify();
        if(empty($event['create_date'])){
            throw new \Exception("请设置创建时间参数");
        }
//        $protocol_no = [
//            $sign_id
//        ];
        $msg_id = Mtool::getOrderNum();
        $newData = [
            'send_time'=>date("Y-m-d H:i:s"),
            'msg_id'=>$msg_id,
            'version'=>$this->defaultConfig['version'],
            'terminal_id'=>$this->config['terminal_id'],
            'txn_type'=>'07',
            'member_id'=>$this->config['member_id'],
            'orig_trans_id'=>$out_trade_no,
            'orig_trade_date'=>$event['create_date'],
//            'txn_amt'=>intval($total_amount*100),
//            'protocol_no'=>$this->AesEncrypt($protocol_no,$aesKey),
//            'risk_item'=>json_encode([
//                'goodsCategory'=>$event['goodsCategory'],
//                'chPayIp'=>Mtool::getIp()
//            ]),
//            'dgtl_envlp'=>$this->encryptByCERFile("01|".$aesKey,$this->config['public_certs_path']),
        ];
//        echo "<pre>";
//        print_r($newData);
//        exit;
        $newData['signature'] = $this->sign($newData);
        $res = $this->post($newData,$this->URL['wx_pub']);
        if(isset($res['biz_resp_code'])&&$res['biz_resp_code']=='0000'){
            return $res;
        }else{
            throw new \Exception($res['biz_resp_msg']);
        }
    }

    /**
     * 退款
     * @param string $out_trade_no 支付订单流水号
     * @param string $out_refund_no 退款订单流水号
     * @param string|int|float $refund_amount 退款金额
     * @param string|null $chanpay_trade_no 可不填
     * @param array $event [refund_reason=退款原因]
     * @return void
     * @throws \Exception
     */
    public function refundOrder(string $out_trade_no,string $out_refund_no,$refund_amount,?string $chanpay_trade_no=null,array $event=[]){
        //验证参数
        $this->verify();
        if(empty($event['refund_reason'])){
            throw new \Exception("请设置退款原因参数");
        }
        $data_content = [
            "txn_sub_type"=> '09',
            "member_id"=> $this->config['member_id'],
            "terminal_id"=> $this->config['terminal_id'],
            "refund_type"=> "8",
            'trans_id'=>$out_trade_no,
            'refund_order_no'=>$out_refund_no,
            'trans_serial_no'=>Mtool::getOrderNum(),
            'refund_reason'=>$event['refund_reason'],
            'refund_amt'=>intval($refund_amount*100),
            'refund_time'=>date("YmdHis"),
        ];
        if(isset($event['notice_url'])){
            $data_content['notice_url'] = $event['notice_url'];
        }
        $data_content = str_replace("\\/", "/",json_encode($data_content));//转JSON
        $newdata = [
            'version'=>$this->defaultConfig['version'],
            'member_id'=>$this->config['member_id'],
            'terminal_id'=>$this->config['terminal_id'],
            'txn_type'=>'331',
            'txn_sub_type'=>'09',
            'data_type'=>'json',
            'data_content'=>$this->encryptByPFXFile($data_content,$this->config['privite_certs_path'],$this->config['key']),
        ];
        $res = $this->post($newdata,"https://vgw.baofoo.com/cutpayment/api/backTransRequest",false);
        $res = $this->decryptByCERFile($res,$this->config['public_certs_path']);
        if(empty($res)){
            throw new \Exception("退款失败");
        }
        if(is_string($res)){
            $res = json_decode($res,true);
        }
        if(isset($res['resp_code'])&&$res['resp_code']=='0000'){
            return $res;
        }else{
            throw new \Exception($res['resp_msg']);
        }
    }

    /**
     * 退款查询
     * @param string $out_trade_no 支付订单流水号,可不填
     * @param string $out_refund_no 退款订单流水号
     * @param string|null $chanpay_trade_no null
     * @param string|null $chanpay_refund_no null
     * @return mixed|string
     * @throws \Exception
     */
    public function refundQuery(string $out_trade_no,string $out_refund_no,?string $chanpay_trade_no=null,?string $chanpay_refund_no = null){
//验证参数
        $this->verify();
        $data_content = [
            "txn_sub_type"=> '10',
            "member_id"=> $this->config['member_id'],
            "terminal_id"=> $this->config['terminal_id'],
            "refund_order_no"=> $out_refund_no,
            "trans_serial_no"=> Mtool::getOrderNum(),
        ];
        if(isset($event['notice_url'])){
            $data_content['notice_url'] = $event['notice_url'];
        }
        $data_content = str_replace("\\/", "/",json_encode($data_content));//转JSON
        $newdata = [
            'version'=>$this->defaultConfig['version'],
            'member_id'=>$this->config['member_id'],
            'terminal_id'=>$this->config['terminal_id'],
            'txn_type'=>'331',
            'txn_sub_type'=>'10',
            'data_type'=>'json',
            'data_content'=>$this->encryptByPFXFile($data_content,$this->config['privite_certs_path'],$this->config['key']),
        ];
        $res = $this->post($newdata,"https://vgw.baofoo.com/cutpayment/api/backTransRequest",false);
        $res = $this->decryptByCERFile($res,$this->config['public_certs_path']);
        if(empty($res)){
            throw new \Exception("退款失败");
        }
        if(is_string($res)){
            $res = json_decode($res,true);
        }
        if(isset($res['resp_code'])&&$res['resp_code']=='0000'){
            return $res;
        }else{
            throw new \Exception($res['resp_msg']);
        }
    }
}